69 反射

678次阅读
没有评论

共计 4982 个字符,预计需要花费 13 分钟才能阅读完成。

引入

1. 什么 反射

  • 反射就是通过字符串来操作类或者对象的属性
  • Python 中一切皆对象, 即都可以使用反射

2. 反射的四个内置函数

反射的本质就是在使用内置函数, 其中反射有以下四个内置函数

  • hasattr : 判断一个属性或方法是否存在这个类中, 返回 bool 值

  • getattr : 获取属性值或者获取方法变量的内存地址 (根据字符串去获取 obj 对象里的对应的方法的内存地址 (加括号 "()" 就可以调用))

  • setattr : 给类或对象设置属性或方法 (通过 setattr 将外部的一个属性或函数绑定到实例中)

  • delattr : 删除类或对象的属性和方法

ps : getattr, hasattr, setattr, delattr 对模块的修改都在内存中进行,并不会影响文件中真实内容

一. 反射模块

1. 模块导入到模块动态导入

  • 之前我们使用模块都是使用 import 的方式进行导入
🍔文件 test24.py

def Foo1():
    print("i am test24_Foo1")

def Foo2():
    print("i am test24_Foo2")

def Foo3():
    print("i am test24_Foo3")
🍔文件 test 反射.py

import test24

test24.Foo1()  # i am test24_Foo1
test24.Foo2()  # i am test24_Foo2
test24.Foo3()  # i am test24_Foo3
  • 通过反射模块的方式动态的导入模块, Python 提供了 __import__ 来实现这一功能
🍔文件 test 反射.py

Inp = input(" 请输入想要导入的模块 >>").strip()
mode = __import__(Inp)  # 通过字符串的方式导入你想要的导的模块
mode.Foo1()
mode.Foo2()
mode.Foo3()
''' 输出
请输入想要导入的模块 >>test24
i am test24_Foo1
i am test24_Foo2
i am test24_Foo3
'''
  • 问题 : 当我们的模块文件路径与执行文件不在同一级, 如下图所示

69 反射

🍔文件 setting.py

def Foo1():
    print("i am setting_Foo1")
🍔文件 test 反射.py

Inp = input(" 请输入想要导入的模块 >>").strip()
mode = __import__(Inp)
mode.Foo1()
''' 输出
请输入想要导入的模块 >>conf.setting
抛出异常 "AttributeError: module 'conf' has no attribute 'Foo1'"
'''
  • 解决方法
Inp = input(" 请输入想要导入的模块 >>").strip()
mode = __import__(Inp,fromlist=True)  # 添加参数
mode.Foo1()
''' 输出
请输入想要导入的模块 >>conf.setting
i am setting_Foo1
'''

2. 第二种反射模块方法

  • 使用 importlib 模块实现 (在 Python3 框架中使用的比较多)
🍔文件 test26.py

def test():
    print("i am test26")
🍔文件 test 反射模块.py

import importlib

Inp = input(" 请输入你要导入的模块 >>").strip()
mode = importlib.import_module(Inp)
mode.test()
''' 输出
请输入你要导入的模块 >>test26
i am test26
'''
  • 当我们的模块文件路径与执行文件 不在同一级, 如下图所示

69 反射

🍔文件 setting.py

def Foo1():
    print("i am setting_Foo1")
import importlib

Inp = input(" 请输入你要导入的模块 >>").strip()
mode = importlib.import_module(Inp)
mode.Foo1()
''' 输出
请输入你要导入的模块 >>conf.setting
i am setting_Foo1
'''

二.hasattr(obj,name)

  • hasattr : 判断一个属性或方法是否存在这个类中, 返回 bool 值
  • hasattr是通过调用 getattr(ojbect, name) 是否抛出异常来实现的
class Person:
    def __init__(self,name,age):
        self.name = name
        self.age = age

    def fight(self):
        print(" 正在 fight")

P1 = Person(" 小王 ",18)

print(hasattr(P1,"name"))  # True
print(hasattr(P1,"age"))   # True
print(hasattr(P1,"fight")) # True
print(hasattr(P1,"aaaa"))  # False 

三.getattr(obj,name)

  • getattr : 获取属性值或者获取方法变量的内存地址
  • 根据字符串去获取 obj 对象里的对应的方法的 内存地址 (加括号 "()" 就可以调用)
🍔续上面模块动态导入补充
Inp = input(" 请输入想要导入的模块 >>").strip()
mode = __import__(Inp,fromlist=True)
method = input(" 请输入你想要执行的方法 >>").strip()
print(getattr(mode,method,None))  # 当在模块中没有找到该方法时, 返回 None, 找到时返回该方法的内存地址
'''🔰存在 -- 输出
请输入想要导入的模块 >>conf.setting
请输入你想要执行的方法 >>Foo1
<function Foo1 at 0x00000214B2964438>
''''''🔰不存在 --- 输出
请输入想要导入的模块 >>conf.setting
请输入你想要执行的方法 >>aaa
None
'''
class Person:
    def __init__(self,name,age):
        self.name = name
        self.age = age

    def fight(self):
        print(" 正在 fight")
        return 123

P1 = Person(" 小王 ",18)

print(getattr(P1,"name",None))   # 小王
print(getattr(P1,"aaaa",None))   # None
# 如果对象中有 "name" 属性, 则打印 "self.name" 的值, 否则打印 "None"

print(getattr(P1,"fight",None))   # [方法的内存地址]
# 如果对象中有 "fight" 方法, 返回该方法的内存地址, 否则返回 "None"

print(getattr(P1,"fight",None)()) # 正在 fight  123
# 加了括号, 存在该方法的话运行该方法并返回该方法的返回值, 否则返回 "None"

print("name" in P1.__dict__)  # True
# 也可以通过 "__dict__" 来进行判断, 效果一样

四.setattr(obj,name,value)

  • setattr : 给类或对象设置属性或方法
  • 通过 setattr 将外部的一个属性或函数绑定到实例中
class Person:
    def __init__(self,name,age):
        self.name = name
        self.age = age

    def fight(self):
        print(" 正在 fight")
        return 123

P1 = Person(" 小王 ",18)

# 设置属性
setattr(P1,"sex","man")
print(getattr(P1,"sex",None))  # man

# 设置方法
def runrun(self):
    print(" 正在跑 ")

setattr(Person,"run",runrun)
getattr(P1,"run",None)()      # 正在跑
# 注意 : 绑定给对象的方法是放在类里面的, 如果使用反射, 则要反射到类的内部

# 如果要将方法反射给对象, 那么就需要手动传入对象 (不推荐这么使用)
def rrr(obj):
    print(obj.name)

setattr(P1,"print_name",rrr)
P1.print_name(P1)  # 小王

五.delattr(obj,name)

  • delattr : 删除类或对象的属性和方法
class Person:
    def __init__(self,name,age):
        self.name = name
        self.age = age

    def fight(self):
        print(" 正在 fight")
        return 123

P1 = Person(" 小王 ",18)

delattr(P1,"name")
print(P1.__dict__)  # {'age': 18}
getattr(P1,"name")  # 报错 "AttributeError" 没有该属性

六. 应用小示例

1. 给空类动态设置属性

class Extend:
    pass

E1 = Extend()

Inp1 = input(" 请输入你需要设置的属性名 >>").strip()
Inp2 = input(" 请输入该属性对应的值 >>").strip()

setattr(E1,Inp1,Inp2)  # 设置属性
print(getattr(E1,Inp1,None))
''' 输出 
请输入你需要设置的属性名 >>name
请输入该属性对应的值 >>shawn
shawn
'''

2. 反射的应用案例

class Person:
    def __init__(self,name,age,sex):
        self.name = name
        self.age = age
        self.sex = sex

    def print_name(self):
        print(f" 名字:{self.name}")

    def print_age(self):
        print(f" 年龄:{self.age}")

    def print_sex(self):
        print(f" 性别:{self.sex}")

    def print_info(self):
        print(self.name,self.age,self.sex)

    def select(self):
        while 1:
            Inp = input(" 请输入需要选择的方法(q 退出)>>").strip()
            if Inp.lower() == "q":break
            if hasattr(self,Inp):   # 判断存不存在
                getattr(self,Inp)() # 拿到内存地址加括号调用
            else:
                print(" 没有该功能 ")

P1 = Person(" 久人旅 ",23,"man")
P1.select()
''' 输出
请输入需要选择的方法(q 退出)>>aaaa
没有该功能
请输入需要选择的方法(q 退出)>>print_name
名字: 久人旅
请输入需要选择的方法(q 退出)>>print_age
年龄:23
请输入需要选择的方法(q 退出)>>print_sex
性别:man
请输入需要选择的方法(q 退出)>>q

Process finished with exit code 0
'''

3. 反射隐藏属性

  • 前面 类的封装 那一篇我们已经介绍过了, 设置隐藏属性的时候会进行变形操作,下面我们与反射组合使用
class Person:
    def __init__(self,name,age,sex):
        self.name = name
        self.age = age
        self.__sex = sex  # 变形 "_Person__sex"

P1 = Person(" 闫帝喵 ",23,"man")

🍔通过反射获取隐藏属性
print(getattr(P1,"__sex"))    # 报错 "AttributeError" 没有该属性
print(getattr(P1,"_Person__sex"))    # man

🍔通过反射设置隐藏属性(为对象设置)
setattr(P1,"_Person__money",4654)
print(getattr(P1,"__money"))  # 报错 "AttributeError" 没有该属性
print(getattr(P1,"_Person__money"))  # 4654

🍔通过反射设置隐藏属性(为类设置)
setattr(Person,"_Person__money",13213)
print(getattr(P1,"__money"))  # 报错 "AttributeError" 没有该属性
print(getattr(P1,"_Person__money"))  # 13213
正文完
 
shawn
版权声明:本站原创文章,由 shawn 2023-06-16发表,共计4982字。
转载说明:除特殊说明外本站文章皆由CC-4.0协议发布,转载请注明出处。
评论(没有评论)